package com.hang.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hang.config.RedisConfig;
import com.hang.entity.Bug;
import com.hang.entity.Message;
import com.hang.exception.MyException;
import com.hang.exception.MyExceptionEnum;
import com.hang.handler.Websocket;
import com.hang.mapper.BugMapper;
import com.hang.mapper.MessageMapper;
import com.hang.mapstruct.BugMapStruct;
import com.hang.model.dto.request.*;
import com.hang.model.vo.BugInfoDetailVO;
import com.hang.model.vo.BugInfoVO;
import com.hang.model.vo.BugInfoVOList;
import com.hang.model.vo.CommentInfoVO;
import com.hang.mongoEntity.BugEntity;
import com.hang.mongoEntity.FileUrlEntity;
import com.hang.service.IBugService;
import com.hang.service.ICommentService;
import lombok.AllArgsConstructor;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 */
@Service
@AllArgsConstructor
public class BugServiceImpl extends ServiceImpl<BugMapper, Bug>
    implements IBugService {
    private RedisConfig redisConfig;

    private BugMapStruct bugMapStruct;

    private MongoTemplate mongoTemplate;

    private ICommentService commentService;

    private Websocket websocket;

    private MessageMapper messageMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createBug(BugModelReq bugModelReq) {
        RedisTemplate<String, Object> redisTemplate = redisConfig.getRedisTemplateByDb(2);

        Bug bug = bugMapStruct.bugModelReqToBug(bugModelReq);

        //提报人和接收人不能是同一人
        if(ObjectUtil.isNotNull(bugModelReq.getReceiveId()) && (bugModelReq.getCreateById().compareTo(bugModelReq.getReceiveId()) == 0)){
            throw new MyException(MyExceptionEnum.CREATE_RECEIVE_REPEAT);
        }

        //对文件存储到扩展类型中
        List<String> fileUrlList = Optional.ofNullable(bugModelReq.getFileUrlList()).orElse(new LinkedList<>());

        //随机生成mongoId
        String mongoId = IdWorker.getIdStr();

        //保存任务内容
        String content = bugModelReq.getContent();
        bug.setMongoId(mongoId);

        mongoTemplate.save(new BugEntity(mongoId,content));

        //保存缺陷附件
        if(!fileUrlList.isEmpty()){
            mongoTemplate.save(new FileUrlEntity(mongoId,fileUrlList));
        }

        //保存mysql数据
        save(bug);

        //redis中保存缺陷数据，方便统计使用
        String key = bugModelReq.getItemId()+"-1";
        redisTemplate.opsForSet().add(key,bug.getBugId());

        //如果指定了接收人，那么生成相关消息并发送给相关人
        Long receiveId = Optional.ofNullable(bugModelReq.getReceiveId()).orElse(null);

        if(ObjectUtil.isNotNull(receiveId)){
            sendMessage(bug.getItemName(),receiveId,bugModelReq.getCreateBy());
        }
    }

    /**
     * 缺陷大厅查找未被接单的缺陷
     * @param conditionDTO
     * @return
     */
    @Override
    public BugInfoVOList getIncompleteBugInHall(ItemConditionDTO conditionDTO) {
        Integer pageNum = conditionDTO.getPageNum();
        Integer pageSize = conditionDTO.getPageSize();

        Long itemId = conditionDTO.getItemId();
        String conditionName = Optional.ofNullable(conditionDTO.getCondition()).orElse("");

        QueryWrapper<Bug> bugQueryWrapper = new QueryWrapper<>();
        bugQueryWrapper.lambda().isNull(Bug::getReceiveId);

        bugQueryWrapper.lambda().eq(Bug::getItemId,itemId);
        if(StrUtil.isNotBlank(conditionName)){
            bugQueryWrapper.and(
                    wrapper -> wrapper
                            .lambda()
                            .like(Bug::getBugTitle,conditionName)
                            .or()
                            .like(Bug::getCreateBy,conditionName)
            );
        }

        bugQueryWrapper.lambda().orderByDesc(Bug::getCreateTime);

        Page<Bug> page = new Page<>(pageNum,pageSize);
        List<Bug> bugList = Optional.ofNullable(this.baseMapper.selectPage(page,bugQueryWrapper).getRecords()).orElse(new LinkedList<>());

        Integer total = Optional.ofNullable(this.baseMapper.selectCount(bugQueryWrapper)).orElse(0);

        return packageBugInfoVO(bugList,total);
    }

    /**
     * 按条件搜索缺陷
     * @param bugConditionDTO
     * @return
     */
    @Override
    public BugInfoVOList getBugListByCondition(BugConditionDTO bugConditionDTO) {
        //用户id
        Long userId = bugConditionDTO.getUserId();

        Long moduleId = bugConditionDTO.getModuleId();

        //分类方式
        Integer type = bugConditionDTO.getType();

        //搜索条件
        String condition = Optional.ofNullable(bugConditionDTO.getCondition()).orElse("");

        //任务类型
        Integer bugType = bugConditionDTO.getBugType();
        //优先级
        Integer priority = bugConditionDTO.getPriority();

        //排序类型
        Integer sortType = bugConditionDTO.getSortType();
        //排序方式
        Integer sort = bugConditionDTO.getSort();

        //解决状态
        Integer solveStatus = bugConditionDTO.getSolveStatus();

        //严重程度
        Integer severity = bugConditionDTO.getSeverity();

        //页码，页距
        Integer pageNum = bugConditionDTO.getPageNum();
        Integer pageSize = bugConditionDTO.getPageSize();

        QueryWrapper<Bug> bugQueryWrapper = new QueryWrapper<>();

        bugQueryWrapper.lambda().eq(Bug::getModuleId,moduleId);
        Page<Bug> page = new Page<>(pageNum,pageSize);

        if(type.compareTo(0) == 0){
            //全部
            bugQueryWrapper.and(
                    wrapper -> wrapper.lambda()
                            .eq(Bug::getCreateById,userId)
                            .or()
                            .eq(Bug::getReceiveId,userId)
            );
        }else if(type.compareTo(1) == 0){
            //我创建的
            bugQueryWrapper.lambda().eq(Bug::getCreateById,userId);
        }else if(type.compareTo(2) == 0){
            //我处理的
            bugQueryWrapper.lambda().eq(Bug::getReceiveId,userId);
        }

        //搜索条件查询
        if(StrUtil.isNotBlank(condition)){
            bugQueryWrapper.and(
                    wrapper -> wrapper.lambda()
                            .like(Bug::getBugTitle,condition)
                            .or()
                            .like(Bug::getCreateBy,condition)
                            .or()
                            .like(Bug::getReceiveName,condition)
            );
        }

        //故障类型
        if(bugType.compareTo(0) != 0){
            bugQueryWrapper.lambda().eq(Bug::getBugType,bugType);
        }

        //优先级
        if(priority.compareTo(0) != 0){
            bugQueryWrapper.lambda().eq(Bug::getPriority,priority);
        }

        //严重程度
        if(severity.compareTo(0) != 0){
            bugQueryWrapper.lambda().eq(Bug::getSeverity,severity);
        }

        //解决状态
        if(solveStatus.compareTo(0) != 0){
            bugQueryWrapper.lambda().eq(Bug::getSolveStatus,solveStatus);
        }

        //排序
        if(sort.compareTo(1) == 0){
            if(sortType.compareTo(1) == 0){
                bugQueryWrapper.lambda().orderByDesc(Bug::getUpdateTime);
            }else if(sortType.compareTo(2) == 0){
                bugQueryWrapper.lambda().orderByDesc(Bug::getPriority);
            }else if(sortType.compareTo(3) == 0){
                bugQueryWrapper.lambda().orderByDesc(Bug::getSeverity);
            }else {
                //溢出的条件按照时间排序
                bugQueryWrapper.lambda().orderByDesc(Bug::getUpdateTime);
            }
        }else {
            if(sortType.compareTo(1) == 0){
                bugQueryWrapper.lambda().orderByAsc(Bug::getUpdateTime);
            }else if(sortType.compareTo(2) == 0){
                bugQueryWrapper.lambda().orderByAsc(Bug::getPriority);
            }else if(sortType.compareTo(3) == 0){
                bugQueryWrapper.lambda().orderByAsc(Bug::getSeverity);
            }else {
                //溢出的条件按照时间排序
                bugQueryWrapper.lambda().orderByAsc(Bug::getUpdateTime);
            }
        }

        List<Bug> bugList = Optional.ofNullable(this.baseMapper.selectPage(page,bugQueryWrapper).getRecords()).orElse(new LinkedList<>());

        Integer total = Optional.ofNullable(this.baseMapper.selectCount(bugQueryWrapper)).orElse(0);
        return packageBugInfoVO(bugList,total);
    }

    /**
     * 更新缺陷信息
     * @param bugModelReq
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateBug(BugModelReq bugModelReq) {
        Long bugId = bugModelReq.getBugId();

        if(ObjectUtil.isNull(bugId)){
            throw new MyException(MyExceptionEnum.ID_IS_NULL);
        }

        //先查询出原有的数据
        Bug dbBug = Optional.ofNullable(getById(bugId)).orElse(null);
        if(ObjectUtil.isEmpty(dbBug)){
            throw new MyException(MyExceptionEnum.DATA_DOES_NOT_EXIST);
        }

        //需要校验是否是本人操作
        if(dbBug.getCreateById().compareTo(bugModelReq.getCreateById()) != 0){
            throw new MyException(MyExceptionEnum.OPERATOR_UPDATE_EXCEPTION);
        }

        //提报人和接收人不能是同一人
        if(ObjectUtil.isNotNull(dbBug.getReceiveId()) && (bugModelReq.getCreateById().compareTo(bugModelReq.getReceiveId()) == 0)){
            throw new MyException(MyExceptionEnum.CREATE_RECEIVE_REPEAT);
        }

        String mongoId = Optional.ofNullable(dbBug.getMongoId()).orElse("");

        //去找相关mongoId中所对应的文档是否有内容需要更新
        Query query = new Query(Criteria.where("_id").is(mongoId));
        Update contentUpdate = new Update().set("content",bugModelReq.getContent());
        mongoTemplate.findAndModify(query,contentUpdate, BugEntity.class);

        //查看链接是否需要更新
        List<String> fileList = Optional.ofNullable(bugModelReq.getFileUrlList()).orElse(new LinkedList<>());

        if(!fileList.isEmpty()){
            Update fileUpdate = new Update().set("fileUrlList",fileList);
            mongoTemplate.findAndModify(query,fileUpdate,FileUrlEntity.class);
        }

        //然后更新数据库中的内容
        Bug bug = bugMapStruct.bugModelReqToBug(bugModelReq);
        bug.setBugId(dbBug.getBugId());

        updateById(bug);

        //发送消息通知
        Long receiveId = Optional.ofNullable(bugModelReq.getReceiveId()).orElse(null);

        if(ObjectUtil.isNotNull(receiveId)){
            sendMessage(bug.getItemName(),receiveId,bugModelReq.getCreateBy());
        }
    }

    /**
     * 批量删除缺陷
     * @param bugDeleteDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBugList(BugDeleteDTO bugDeleteDTO) {
        Long userId = bugDeleteDTO.getUserId();
        List<Long> bugIdList = bugDeleteDTO.getBugIdList();

        if(ObjectUtil.isNull(userId)){
            throw new MyException(MyExceptionEnum.ID_IS_NULL);
        }

        if(ObjectUtil.isNull(bugIdList) || bugIdList.isEmpty()){
            throw new MyException(MyExceptionEnum.ONE_MORE_DELETE);
        }

        //校验是否存在不属于自己创建的缺陷
        List<Bug> bugList = Optional.ofNullable(this.lambdaQuery().in(Bug::getBugId, bugIdList).list()).orElse(new LinkedList<>());

        Set<Long> distinctIdList = bugList.stream().map(Bug::getCreateById).collect(Collectors.toSet());

        //判断集合中是否有不属于这个用户id，有则不允许删除
        distinctIdList.forEach(
                id ->{
                    if(id.compareTo(userId) != 0){
                        throw new MyException(MyExceptionEnum.OPERATOR_DELETE_EXCEPTION);
                    }
                }
        );

        //只删除mysql中的数据
        removeByIds(bugIdList);

        //移除redis中相关的缺陷
        RedisTemplate<String, Object> redisTemplate = redisConfig.getRedisTemplateByDb(2);
        bugList.forEach(
                bug -> {
                    for (int i = 1; i <= 3 ; i++) {
                        String key = bug.getItemId() + "-" +i;
                        if(redisTemplate.opsForSet().isMember(key,bug.getBugId())){
                            redisTemplate.opsForSet().remove(key,bug.getBugId());
                        }
                    }
                }
        );
    }

    /**
     * 查看缺陷详情
     * @param bugId
     * @return
     */
    @Override
    public BugInfoDetailVO getDetail(Long bugId) {
        if(ObjectUtil.isNull(bugId)){
            throw new MyException(MyExceptionEnum.ID_IS_NULL);
        }

        Bug dbBug = Optional.ofNullable(getById(bugId)).orElse(null);

        if(ObjectUtil.isEmpty(dbBug)){
            throw new MyException(MyExceptionEnum.DATA_DOES_NOT_EXIST);
        }

        //查看缺陷详情
        BugInfoDetailVO bugInfoDetailVO = bugMapStruct.bugToBugInfoDetailVO(dbBug);

        String mongoId = Optional.ofNullable(dbBug.getMongoId()).orElse("");

        if(StrUtil.isNotBlank(mongoId)){
            Query query = new Query(Criteria.where("_id").is(mongoId));
            BugEntity bugEntity = Optional.ofNullable(mongoTemplate.findOne(query, BugEntity.class)).orElse(new BugEntity());

            bugInfoDetailVO.setContent(Optional.ofNullable(bugEntity.getContent()).orElse(""));

            FileUrlEntity fileUrlEntity = Optional.ofNullable(mongoTemplate.findOne(query, FileUrlEntity.class)).orElse(new FileUrlEntity());
            bugInfoDetailVO.setFileUrlList(Optional.ofNullable(fileUrlEntity.getFileUrlList()).orElse(new LinkedList<>()));
        }

        List<CommentInfoVO> commentInfoVOList = commentService.getCommonListInfo(bugId,3);
        bugInfoDetailVO.setCommentInfoVOList(commentInfoVOList);

        return bugInfoDetailVO;
    }

    /**
     * 修改缺陷状态
     * @param bugUpdateReq
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateBugStatus(BugUpdateReq bugUpdateReq) {
        Long bugId = bugUpdateReq.getBugId();
        Integer solveStatus = bugUpdateReq.getSolveStatus();
        Long receiveId = bugUpdateReq.getReceiveId();

        Bug dbBug = Optional.ofNullable(getById(bugId)).orElse(null);

        if(ObjectUtil.isEmpty(dbBug)){
            throw new MyException(MyExceptionEnum.DATA_DOES_NOT_EXIST);
        }

        //校验是否是自己需要处理的任务
        Long userId = dbBug.getReceiveId();

        if(userId.compareTo(receiveId) != 0){
            throw new MyException(MyExceptionEnum.OPERATOR_BUG_EXCEPTION);
        }

        Integer oldSolveStatus = dbBug.getSolveStatus();
        //校验缺陷状态是否改变
        if(solveStatus.compareTo(oldSolveStatus) == 0){
            throw new MyException(MyExceptionEnum.BUG_STATUS_SAME);
        }

        //修改缺陷状态
        dbBug.setSolveStatus(solveStatus);

        //对处理缺陷时间进行记录
        LocalDateTime now = LocalDateTime.now();
        ZoneId zoneId = ZoneId.systemDefault();

        ZonedDateTime zonedDateTime = now.atZone(zoneId);
        Date date = Date.from(zonedDateTime.toInstant());

        dbBug.setSolveTime(date);

        updateById(dbBug);

        //redis修改相应的故障状态
        RedisTemplate<String, Object> redisTemplate = redisConfig.getRedisTemplateByDb(2);

        String oldKey = dbBug.getItemId() + "-" +oldSolveStatus;
        String newKey = dbBug.getItemId() + "-" +solveStatus;

        redisTemplate.opsForSet().remove(oldKey,bugId);
        redisTemplate.opsForSet().add(newKey,bugId);
        //发送消息通知
        sendUpdateMessage(dbBug.getItemName(),dbBug.getCreateById(),Optional.ofNullable(dbBug.getReceiveName()).orElse(""),dbBug.getBugTitle());
    }

    /**
     * 缺陷接单
     * @param bugReceiveDTO
     */
    @Override
    public void receivingBug(BugReceiveDTO bugReceiveDTO) {
        Long bugId = bugReceiveDTO.getBugId();
        Long userId = bugReceiveDTO.getUserId();
        String realName = bugReceiveDTO.getRealName();

        //获取缺陷
        Bug dbBug = Optional.ofNullable(getById(bugId)).orElse(null);

        if(ObjectUtil.isEmpty(dbBug)){
            throw new MyException(MyExceptionEnum.DATA_DOES_NOT_EXIST);
        }

        //校验接单人和提报人是否是同一个人
        if(userId.compareTo(dbBug.getCreateById()) == 0){
            throw new MyException(MyExceptionEnum.BUG_PERSON_SAME);
        }

        //接取任务
        dbBug.setReceiveId(userId);
        dbBug.setReceiveName(realName);

        updateById(dbBug);

        //发送消息给创建人
        sendReceiveMessage(dbBug.getItemName(),dbBug.getCreateById(),realName,dbBug.getBugTitle());
    }

    /**
     * 删除超过14天的已完成的缺陷
     * @return
     */
    @Override
    public List<Bug> getFinishBugList() {
        LocalDateTime passTime = LocalDateTime.now().plusDays(-14L);
        ZoneId zoneId = ZoneId.systemDefault();

        ZonedDateTime passZonedDateTime = passTime.atZone(zoneId);

        Date passDate = Date.from(passZonedDateTime.toInstant());

        return Optional.ofNullable(this.lambdaQuery().eq(Bug::getSolveStatus,3).lt(Bug::getSolveTime,passDate).list()).orElse(new LinkedList<>());
    }

    private BugInfoVOList packageBugInfoVO(List<Bug> bugList, Integer total) {
        BugInfoVOList bugInfoVOList = new BugInfoVOList();
        List<BugInfoVO> bugInfoVOS = bugMapStruct.bugListToBugInfoVOList(bugList);

        bugInfoVOList.setTotal(total);
        bugInfoVOList.setBugInfoVOList(bugInfoVOS);

        return bugInfoVOList;
    }

    private void sendReceiveMessage(String itemName,Long createById, String realName, String bugTitle) {
        //生成消息并发送
        Message message = new Message();
        message.setTitle("缺陷接取通知");
        message.setContent("您所在的项目【"+itemName+"】有新的消息:"+realName + "接取了您的缺陷任务，缺陷名为"+bugTitle+",可以留意您的缺陷处理情况");
        message.setReceiveId(createById);

        //推送通知
        try {
            websocket.sendMessage(message);
        } catch (IOException e) {
            log.error(e.toString());
        }

        //保存消息
        messageMapper.insert(message);
    }

    private void sendUpdateMessage(String itemName,Long createById, String receiveName, String bugTitle) {
        //生成消息并发送
        Message message = new Message();
        message.setTitle("缺陷更新通知");
        message.setContent("您所在的项目【"+itemName+"】有新的消息:"+receiveName + "更新了您创建的缺陷状态，缺陷名为"+bugTitle+",请到您的缺陷列表中查看~");
        message.setReceiveId(createById);

        //推送通知
        try {
            websocket.sendMessage(message);
        } catch (IOException e) {
            log.error(e.toString());
        }

        //保存消息
        messageMapper.insert(message);
    }

    private void sendMessage(String itemName,Long receiveId,String createBy){
        //生成消息并发送
        Message message = new Message();
        message.setTitle("缺陷通知");
        message.setContent("您所在的项目【"+itemName+"】有新的消息:"+createBy + "给您提交了一个新的缺陷,请到您的缺陷列表中查看~");
        message.setReceiveId(receiveId);

        //推送通知
        try {
            websocket.sendMessage(message);
        } catch (IOException e) {
            log.error(e.toString());
        }

        //保存消息
        messageMapper.insert(message);
    }
}




